﻿using Engine;
using Engine.Graphics;
using Engine.Input;
using Engine.Media;
using Engine.Serialization;
using Game;
using NekoMeko.Common;
using Neorxna;
using Neorxna.Common;
using Neorxna.NeoModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Xml.Linq;
using ZipArchive = Game.ZipArchive;
using ZipArchiveEntry = Game.ZipArchiveEntry;

namespace NekoMeko
{
	public static class NekoMekoDataManager
	{
		public const string DefaultMaleModelKey = "Model-ScMale-BoneSet-ScMale";
		public const string DefaultFemaleModelKey = "Model-ScFemale-BoneSet-ScFemale";
		public const string DefaultMaleSkinKey = "Skin-Walter-ModelKey-ScMale";
		public const string DefaultFemaleSkinKey = "Skin-Doris-ModelKey-ScFemale";
		public static string CreatingPlayerModelKey
		{//表示正在创建且待应用到实例组件上的修改
			get; set;
		}
		public static string CreatingPlayerSkinKey
		{//表示正在创建且待应用到实例组件上的修改
			get; set;
		}
		public static int EditingPlayerIndex
		{//表示正在创建且的修改对应的玩家数据id；用于解决修改还未应用到实例上时的PlayerScreen和PlayersScreen头像等界面的模型没跟随修改的问题
			get; set;
		} = -1;
		static readonly List<NekoResModel> m_models = [];
		static readonly List<NekoResSkin> m_skins = [];
		static readonly Dictionary<string, NekoResModel> m_modelsByKey = [];
		static readonly Dictionary<string, NekoResSkin> m_skinsByKey = [];

		static readonly Dictionary<string, BoneSet> m_boneSets = [];

		public static ReadOnlyList<NekoResModel> AllModelsData => new(m_models);
		public static ReadOnlyList<NekoResSkin> AllSkinsData => new(m_skins);
		public static Dictionary<string, Model> ModelsInResPack
		{
			get; set;
		} = [];
		public static Dictionary<string, Texture2D> TexturesInResPack
		{
			get; set;
		} = [];

		internal static void Initialize()
		{
			foreach (ContentInfo contentInfo in ContentManager.List("NekoMekoRes/NekoResModel"))
			{//从Assets加载模型数据
				if (string.IsNullOrEmpty(contentInfo.ContentSuffix))
					continue;
				string suffix = contentInfo.ContentSuffix.StartsWith('.') ? contentInfo.ContentSuffix.Substring(1) : contentInfo.ContentSuffix;//获取不带点的文件后缀
				if (suffix != "json")
					continue;
				try
				{
					string json = new StreamReader(contentInfo.Duplicate()).ReadToEnd().ToString();
					var model = JsonSerializer.Deserialize<NekoResModel>(json, Converts.JsonSerializerOptions);
					if (model != null && !string.IsNullOrEmpty(model.Key))
					{
						if (m_modelsByKey.ContainsKey(model.Key))
						{
							Debug.LogError($"There's repeated key \"{model.Key}\" in NekoResModel data.");
							continue;
						}
						if (!string.IsNullOrEmpty(model.Gender) && model.Gender != "Male" && model.Gender != "Female")
						{
							Debug.LogError($"Unknow gender \"{model.Gender}\". Must be \"Male\" or \"Female\" or empty if no limit.");
							continue;
						}
						m_models.Add(model);
						m_modelsByKey.Add(model.Key, model);
					}
				}
				catch (Exception e)
				{
					Debug.LogError($"Failed to load NekoResModel from {contentInfo.AbsolutePath}: {e.Message}");
					continue;
				}
			}
			foreach (ContentInfo contentInfo in ContentManager.List("NekoMekoRes/NekoResSkin"))
			{//从Assets加载皮肤数据
				if (string.IsNullOrEmpty(contentInfo.ContentSuffix))
					continue;
				string suffix = contentInfo.ContentSuffix.StartsWith('.') ? contentInfo.ContentSuffix.Substring(1) : contentInfo.ContentSuffix;//获取不带点的文件后缀
				if (suffix != "json")
					continue;
				try
				{
					string json = new StreamReader(contentInfo.Duplicate()).ReadToEnd().ToString();
					var skin = JsonSerializer.Deserialize<NekoResSkin>(json, Converts.JsonSerializerOptions);
					if (skin != null && !string.IsNullOrEmpty(skin.Key))
					{
						if (m_skinsByKey.ContainsKey(skin.Key))
						{
							Debug.LogError($"There's repeated key \"{skin.Key}\" in NekoResSkin data.");
							continue;
						}
						m_skins.Add(skin);
						m_skinsByKey.Add(skin.Key, skin);
					}
				}
				catch (Exception e)
				{
					Debug.LogError($"Failed to load NekoResSkin from {contentInfo.AbsolutePath}: {e.Message}");
					continue;
				}
			}
			foreach (ContentInfo contentInfo in ContentManager.List("NekoBoneSet"))
			{//从Assets加载骨架数据
				if (string.IsNullOrEmpty(contentInfo.ContentSuffix))
					continue;
				string suffix = contentInfo.ContentSuffix.StartsWith('.') ? contentInfo.ContentSuffix.Substring(1) : contentInfo.ContentSuffix;//获取不带点的文件后缀
				if (suffix != "json")
					continue;
				try
				{
					string json = new StreamReader(contentInfo.Duplicate()).ReadToEnd().ToString();
					var boneSet = JsonSerializer.Deserialize<BoneSet>(json, Converts.JsonSerializerOptions);
					if (boneSet != null && !string.IsNullOrEmpty(boneSet.Name))
					{
						if (m_boneSets.ContainsKey(boneSet.Name))
						{
							Debug.LogDebug($"There's repeated key \"{boneSet.Name}\" in BoneSets data.");
							continue;
						}
						m_boneSets.Add(boneSet.Name, boneSet);
					}
				}
				catch (Exception e)
				{
					Debug.LogError($"Failed to load BoneSet from {contentInfo.AbsolutePath}: {e.Message}");
					continue;
				}
			}
			LoadFromResPack();
		}

		static void LoadFromResPack()
		{//从Neorxna资源目录加载资源包
			foreach (string fileName in Storage.ListFileNames(NeorxnaResources.NeorxnaResPackDirectory))
			{
				if (Storage.GetExtension(fileName).ToLower() == ".scneko")
				{
					string zipPath = Storage.CombinePaths(NeorxnaResources.NeorxnaResPackDirectory, fileName);
					using Stream sourceStream = Storage.OpenFile(zipPath, OpenFileMode.Read);
					using var zipArchive = ZipArchive.Open(sourceStream, keepStreamOpen: true);

					List<string> modelResNameInZip = [];
					List<string> textureResNameInZip = [];

					foreach (ZipArchiveEntry item2 in zipArchive.ReadCentralDir())
					{//第一次遍历压缩包，获取所有 使用相对路径的ResPath字符串
						string filenameInZip = item2.FilenameInZip;
						//Debug.LogDebug(filenameInZip);
						bool isModelData = filenameInZip == "Model.json";
						bool isSkinData = filenameInZip.StartsWith("Skin") && filenameInZip.EndsWith(".json");
						bool isBoneSetData = filenameInZip == "BoneSet.json";
						if (!isModelData && !isSkinData && !isBoneSetData)
							continue;
						string pathInZip = Storage.CombinePaths(zipPath, filenameInZip);
						MemoryStream memoryStream = new();
						zipArchive.ExtractFile(item2, memoryStream);
						memoryStream.Position = 0L;
						try
						{
							string json = ModsManager.StreamToString(memoryStream);
							if (isModelData)
							{
								var modelData = JsonSerializer.Deserialize<NekoResModel>(json, Converts.JsonSerializerOptions);
								if (modelData != null && !string.IsNullOrEmpty(modelData.Key))
								{
									if (m_modelsByKey.ContainsKey(modelData.Key))
									{
										Debug.LogError($"There's repeated key \"{modelData.Key}\" in NekoResModel data.");
										continue;
									}
									if (!string.IsNullOrEmpty(modelData.Gender) && modelData.Gender != "Male" && modelData.Gender != "Female")
									{
										Debug.LogError($"Unknow gender \"{modelData.Gender}\". Must be \"Male\" or \"Female\" or empty if no limit.");
										continue;
									}
									foreach (var item in modelData.ResPath)
									{//检查是否存在相对路径（资源位于资源包中）
										string key = item.Key, resPath = item.Value;
										if (resPath.StartsWith('/') || resPath.StartsWith('\\'))
										{
											resPath = resPath.Substring(1);
											modelResNameInZip.Add(resPath);
											modelData.ResPath[key] = Storage.CombinePaths(zipPath, resPath);//将完整路径应用更改到数据中便于后续精确定位
										}
									}
									m_models.Add(modelData);
									m_modelsByKey.Add(modelData.Key, modelData);
								}
							}
							else if (isSkinData)
							{
								var skinData = JsonSerializer.Deserialize<NekoResSkin>(json, Converts.JsonSerializerOptions);
								if (skinData != null && !string.IsNullOrEmpty(skinData.Key))
								{
									if (m_skinsByKey.ContainsKey(skinData.Key))
									{
										Debug.LogError($"There's repeated key \"{skinData.Key}\" in NekoResSkin data.");
										continue;
									}
									foreach (var item in skinData.ResPath)
									{//检查是否存在相对路径（资源位于资源包中）
										string key = item.Key, resPath = item.Value;
										if (resPath.StartsWith('/') || resPath.StartsWith('\\'))
										{
											resPath = resPath.Substring(1);
											textureResNameInZip.Add(resPath);
											skinData.ResPath[key] = Storage.CombinePaths(zipPath, resPath);//将完整路径应用更改到数据中便于后续精确定位
										}
									}
									m_skins.Add(skinData);
									m_skinsByKey.Add(skinData.Key, skinData);
								}
							}
							else if (isBoneSetData)
							{
								var bonSetData = JsonSerializer.Deserialize<BoneSet>(json, Converts.JsonSerializerOptions);
								if (bonSetData != null && !string.IsNullOrEmpty(bonSetData.Name))
								{
									if (m_boneSets.ContainsKey(bonSetData.Name))
									{
										Debug.LogDebug($"There's repeated key \"{bonSetData.Name}\" in BoneSets data.");
										continue;
									}
									m_boneSets.Add(bonSetData.Name, bonSetData);
								}
							}
						}
						catch (Exception e)
						{
							Debug.LogError($"Failed to load NekoMeko ResPack from {pathInZip}: {e.Message}");
						}
						finally
						{
							memoryStream.Dispose();
						}
					}
					foreach (ZipArchiveEntry item2 in zipArchive.ReadCentralDir())
					{//第二次遍历压缩包，加载资源
						string filenameInZip = item2.FilenameInZip;
						string pathInZip = Storage.CombinePaths(zipPath, filenameInZip);
						foreach (var modelResName in modelResNameInZip)
						{
							if (filenameInZip == modelResName && !ModelsInResPack.ContainsKey(pathInZip))//已加载过的资源无需重复加载
							{
								MemoryStream memoryStream = new();
								zipArchive.ExtractFile(item2, memoryStream);
								memoryStream.Position = 0L;
								try
								{
									Model model = Model.Load(memoryStream, true);
									ModelsInResPack.Add(pathInZip, model);
									Debug.LogDetails($"Model \"{modelResName}\" in ResPack loaded.");
								}
								catch (Exception e)
								{
									Debug.LogError($"Load Model \"{pathInZip}\" in ResPack failed. Reason: {e.Message}");
								}
								finally
								{
									memoryStream.Dispose();
								}
							}
						}
						foreach (var skinResName in textureResNameInZip)
						{
							if (filenameInZip == skinResName && !TexturesInResPack.ContainsKey(pathInZip))//已加载过的资源无需重复加载
							{
								MemoryStream memoryStream = new();
								zipArchive.ExtractFile(item2, memoryStream);
								memoryStream.Position = 0L;
								try
								{
									Texture2D texture = NeorxnaResources.LoadTexture(memoryStream);
									TexturesInResPack.Add(pathInZip, texture);
									Debug.LogDetails($"Texture \"{skinResName}\" in ResPack loaded.");
								}
								catch(Exception e)
								{
									Debug.LogError($"Load Texture \"{pathInZip}\" in ResPack failed. Reason: {e.Message}");
								}
								finally
								{
									memoryStream.Dispose();
								}
							}
						}
					}

				}
			}
		}
		public static NekoResModel FindModelData(string key, bool throwOnError = false)
		{
			if (m_modelsByKey.TryGetValue(key, out NekoResModel model))
				return model;
			return throwOnError ? throw new ArgumentException($"NekoResModel with key \"{key}\" not found.") : null;
		}
		public static NekoResSkin FindSkinData(string key, bool throwOnError = false)
		{
			if (m_skinsByKey.TryGetValue(key, out NekoResSkin skin))
				return skin;
			return throwOnError ? throw new ArgumentException($"NekoResSkin with key \"{key}\" not found.") : null;
		}

		public static BoneSet FindBoneSet(string name, bool throwOnError = false)
		{
			if (m_boneSets.TryGetValue(name, out var boneSet))
				return boneSet;
			return throwOnError ? throw new ArgumentException($"BoneSet with name \"{name}\" not found.") : null;
		}
		public static string GetSkinKeyByCharacterSkinName(string skinName)
		{
			return skinName switch
			{
				"$Male1" => "Skin-Walter-ModelKey-ScMale",
				"$Male2" => "Skin-Basil-ModelKey-ScMale",
				"$Male3" => "Skin-Geoffrey-ModelKey-ScMale",
				"$Male4" => "Skin-Zachary-ModelKey-ScMale",
				"$Female1" => "Skin-Doris-ModelKey-ScFemale",
				"$Female2" => "Skin-Mabel-ModelKey-ScFemale",
				"$Female3" => "Skin-Ada-ModelKey-ScFemale",
				"$Female4" => "Skin-Shirley-ModelKey-ScFemale",
				_ => string.Empty,
			};
		}

		public static string GetCharacterSkinNameByTextureName(string textureName)
		{
			if (!string.IsNullOrEmpty(textureName) && textureName.Contains('/'))
				textureName = textureName.Split('/')[^1];
			return textureName switch
			{
				"HumanMale1" => "$Male1",
				"HumanMale2" => "$Male2",
				"HumanMale3" => "$Male3",
				"HumanMale4" => "$Male4",
				"HumanFemale1" => "$Female1",
				"HumanFemale2" => "$Female2",
				"HumanFemale3" => "$Female3",
				"HumanFemale4" => "$Female4",
				_ => "$Female1",
			};
		}

	}
}
